#include "Task.hpp"
#include <string>
#include <vector>
#include <unistd.h>
#include <cstdlib>
#include <cassert>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>

const int processNum = 10;
std::vector<task_t> tasks;

// 管理通信信道 --- 先描述
class channel
{
public:
    channel(int cmdfd, pid_t slaverId, const std::string processName)
        : _cmdfd(cmdfd), _slaverId(slaverId), _processName(processName)
    {}

    int _cmdfd;               // 发送任务的文件描述符
    pid_t _slaverId;          // 子进程 pid
    std::string _processName; // 子进程名字
};

void slaver()
{
    while (true)
    {
        int cmdCode = 0;
        // 规定通信协议：一次读 4 bytes
        int n = read(0, &cmdCode, sizeof(int));     // 因为对管道的读端做了重定向到标准输入，因此都是从 0 fd中读取
        if (n == sizeof(int))
        {
            std::cout << "[slaver process-" << getpid() << "] get a command, cmdcode: " << cmdCode << std::endl;
            if (cmdCode >= 0 && cmdCode < tasks.size()) 
                tasks[cmdCode]();
        }
        if (n == 0) break;       // 读到为结尾的本质是父进程不再往管道写入数据了，因此子进程可以退出了，没有必要继续读取了。
    }
}

// 指定命令标准：
// 输入型参数：const &
// 输出：*
// both: &
void InitProcessPool(std::vector<channel> *channels)
{
    // version 2: 确保每一个子进程都只有一个写端
    std::vector<int> oldfds;    // 记录父进程打开的所有fd，即管道写端

    for (int i = 0; i < processNum; ++i)
    {
        int pipefd[2];      
        int n = pipe(pipefd);       // 提前创建管道
        assert(n == 0);
        (void)n;

        pid_t id = fork();  
        // child process
        if (id == 0)
        {
            // 关闭之前所有管道的写端
            for(auto fd : oldfds) close(fd);  

            close(pipefd[1]);       // 关闭子进程对管道的写端
            dup2(pipefd[0], 0);     // 子进程对管道的读端做重定向到标准输入，弱化管道的概念，read 直接读 0 fd即可
            close(pipefd[0]);       // 这一步可做可不做

            slaver();     
            std::cout << "child process(" << getpid() << ") quit!\n";      
            exit(0);            
        }
        // father process
        close(pipefd[0]);       // 关闭父进程对管道的读端

        // 添加 channel 字段
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name));

        oldfds.push_back(pipefd[1]);
    }
}

void DEBUG(const std::vector<channel> &channels)
{
    // test
    for (const auto &v : channels)
        std::cout << v._cmdfd << " " << v._slaverId << " " << v._processName << std::endl;
}

void Menu()
{
    std::cout << " -------------------------------------" << std::endl;
    std::cout << "|   1. task1             2. task2     |" << std::endl;
    std::cout << "|   3. task3             4. task4     |" << std::endl;
    std::cout << "|             0. exit                 |" << std::endl;
    std::cout << " ------------------------------------- " << std::endl;
}


void ContralSlaver(const std::vector<channel> &channels)
{
    int which = 0;      
    while (true)
    {
        Menu();
        int input = 0;
        std::cout << "Please Enter@ ";
        std::cin >> input;

        if(input <= 0 || input > tasks.size()) break;

        // 1. 选择任务
        // int cmdCode = rand() % tasks.size();
        int cmdCode = input - 1;

        // 2. 选择进程(这一步需要保证负载平衡，可以采用随机数或者轮询的策略)
        // int processId = rand() % channels.size();

        std::cout << "[father process]# " << "cmdcode: " << cmdCode 
                << " is already send to [" << channels[which]._slaverId << "] process name: "
                << channels[which]._processName << std::endl;

        // 3. 发送任务
        // 规定通信协议：一次写 4 bytes
        write(channels[which]._cmdfd, &cmdCode, sizeof(cmdCode));

        ++which;
        which %= channels.size();       // 在选择进程执行任务时，采用轮询的策略
        // sleep(1);
    }
}

void QuitProcess(const std::vector<channel> &channels)
{
    // // version1 
    // for(int i = channels.size() - 1; i >= 0; --i)
    // {
    //     close(channels[i]._cmdfd);
    //     waitpid(channels[i]._slaverId, nullptr, 0);
    // }

    // version2
    for (const auto &c : channels)
	{
		close(c._cmdfd);
		waitpid(c._slaverId, nullptr, 0);
	}

    // for (const auto &c : channels) close(c._cmdfd);
    // sleep(5);
    // for (const auto &c : channels) waitpid(c._slaverId, nullptr, 0);
}

int main()
{
    LoadTask(&tasks);
    srand(time(nullptr) ^ getpid() ^ 1023);

    // 管理通信信道 --- 后组织
    // father -> w, child -> r
    std::vector<channel> channels;
    // 1. 初始化
    InitProcessPool(&channels);

    // 2. 开始控制子进程
    ContralSlaver(channels);

    // 3. 释放资源
    QuitProcess(channels);
    return 0;
}